
function to2 (number) {
  if (typeof number === 'number') {
    return parseFloat(number.toFixed(2))
  } else {
    return number;
  }
}

class Accrual {

  constructor({cr=0, dr=0, swapped=false}={}) {
    this.cr = to2(cr);
    this.dr = to2(dr);
    this.swapped = swapped;
  }

  swap(){
    const {cr, dr} = this;
    if (cr < 0 || dr < 0) {
      this.cr = to2(-dr);
      this.dr = to2(-cr);
      this.swapped = true;
    }
  }

  unswap() {
    const {cr, dr} = this;
    if (this.swapped) {
      this.cr = to2(-dr);
      this.dr = to2(-cr);
      this.swapped = false;
    }
  }

  diff(dir=0){
    const {cr, dr} = this;
    return to2(dir < 0 ? cr - dr : dr - cr);
  }

  dir() {
    const {cr, dr} = this;
    if (dr === 0 && cr !== 0) {
      return 'cr';
    } else if (cr === 0 && dr !== 0) {
      return 'dr';
    } else if (cr === 0 && dr === 0){
      return 'zero';
    } else {
      return 'both';
    }
  }

  oppoDir() {
    return {
      cr: 'dr',
      dr: 'cr'
    }[this.dir()];
  }

  add({cr=0, dr=0}={}) {
    this.cr = to2(this.cr + cr);
    this.dr = to2(this.dr + dr);
    return this;
  }

  net(){
    const {cr, dr} = this;
    const base = Math.min(cr, dr);
    this.cr = to2(cr - base);
    this.dr = to2(dr - base);
    return this;
  }

  copy() {
    return new Accrual(this);
  }

  oppo() {
    return new Accrual({
      cr: this.dr,
      dr: this.cr
    })
  }

  decomposeAgainst(accrualList) {

    let crSum = this.cr;
    let drSum = this.dr;

    const list = [];
    for (let {cr, dr} of accrualList) {

      const deductedCr = Math.min(dr, crSum);
      const deductedDr = Math.min(cr, drSum);

      list.push(new Accrual({cr: deductedCr, dr: deductedDr}));

      crSum -= deductedCr;
      drSum -= deductedDr;
    }

    return {
      decomp: list.filter(({cr, dr}) => !(cr === 0 && dr === 0)),
      rest: new Accrual({cr: crSum, dr: drSum})
    };
  }
}

export default Accrual;